/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * Original source from Google Web Toolkit(GWT) 
 * http://code.google.com/webtoolkit/
 *
 * Modified to adapt OpenWAF Framework 
 */

/* Original GWT License */

/*
 * Copyright 2008 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package java.lang;

import java.io.Serializable;

public abstract class Number implements Serializable {

    protected static Object floatRegex;

    
    static class __Digits {

        final static char[] digits = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
            'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
            's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    }

    static class __ParseLong {

        private static final int[] maxDigitsForRadix = {-1, -1, // unused
            30, // base 2
            19, // base 3
            15, // base 4
            13, // base 5
            11, 11, // base 6-7
            10, // base 8
            9, 9, // base 9-10
            8, 8, 8, 8, // base 11-14
            7, 7, 7, 7, 7, 7, 7, // base 15-21
            6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // base 22-35
            5 // base 36
        };
        private static final int[] maxDigitsRadixPower = new int[37];
        private static final int[] maxLengthForRadix = {-1, -1, // unused
            63, // base 2
            40, // base 3
            32, // base 4
            28, // base 5
            25, // base 6
            23, // base 7
            21, // base 8
            20, // base 9
            19, // base 10
            19, // base 11
            18, // base 12
            18, // base 13
            17, // base 14
            17, // base 15
            16, // base 16
            16, // base 17
            16, // base 18
            15, // base 19
            15, // base 20
            15, // base 21
            15, // base 22
            14, // base 23
            14, // base 24
            14, // base 25
            14, // base 26
            14, // base 27
            14, // base 28
            13, // base 29
            13, // base 30
            13, // base 31
            13, // base 32
            13, // base 33
            13, // base 34
            13, // base 35
            13 // base 36
        };
        /**
         * A table of floor(MAX_VALUE / maxDigitsRadixPower).
         */
        private static final long[] maxValueForRadix = new long[37];

        static {
            for (int i = 2; i <= 36; i++) {
                maxDigitsRadixPower[i] = (int) Math.pow(i, maxDigitsForRadix[i]);
                maxValueForRadix[i] = Long.MAX_VALUE / maxDigitsRadixPower[i];
            }
        }
    }

    protected static long getIntValue(String s, int lowerBound, int upperBound)
            throws NumberFormatException {
        return parseIntValue(s, lowerBound,  upperBound);
    }

    protected static int getRadix(String s) {        
        if (s.startsWith("-"))
            s = s.substring(1);
        if (s.startsWith("0x") || s.startsWith("0X") || s.startsWith("#")) 
            return 16;
        if (s.startsWith("0"))
            return 8;
        return 10;      
    }
    protected static String getNumberStringWithoutRadix(String s) {        
        if (s.startsWith("-"))
            s = s.substring(1);        
        if (s.startsWith("0x") || s.startsWith("0X"))
            return s.substring(2);
        if(s.startsWith("#"))
            return s.substring(1);
        return s;

    }

    protected static double parseDoubleValue(String s)
            throws NumberFormatException {
        double value = nativeParseDouble(s);
        if (nativeIsNaN(value)) {
            throw new NumberFormatException("Invalide Input String :"+s);
        }
        return value;
    }
    protected static int parseIntValue(String s, int radix,int lowerBound, int upperBound)
                        throws NumberFormatException {
        boolean negative=false;
        String chars=s;
        if(s.startsWith("-")){
            negative=true;
            chars=s.substring(1);
        }
        chars=getNumberStringWithoutRadix(chars);
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix + " out of range");
        }

        int l = chars.length();

        for (int i = 0; i < l; i++) {
            if (Character.digit(chars.charAt(i), radix) == -1) {
                 throw new NumberFormatException("Invalide Input String :"+s);
            }
        }
        int value = nativeParseInt(chars, radix);
        if (nativeIsNaN(value)) {
            throw new NumberFormatException("Invalide Input String :"+s);
        } else if (value < lowerBound || value > upperBound) {
            throw new NumberFormatException("Invalide Input String :"+s);
        }
        if(negative){
            return -value;
        }
        return value;


    }

    protected static int parseIntValue(String s, int lowerBound, int upperBound)
                        throws NumberFormatException {

        if (s == null) {
            throw new NumberFormatException("null");
        }                     
        return parseIntValue(s,getRadix(s), lowerBound, upperBound);

    }

    protected static long __parseAndValidateLong(String s, int radix)
            throws NumberFormatException {

        if (s == null) {
            throw new NumberFormatException("null");
        }
        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
            throw new NumberFormatException("radix " + radix + " out of range");
        }

        int length = s.length();
        boolean negative = (length > 0) && (s.charAt(0) == '-');
        if (negative) {
            s = s.substring(1);
            length--;
        }
        if (length == 0) {
           throw new NumberFormatException("Invalide Input String :"+s);
        }
        while (s.length() > 0 && s.charAt(0) == '0') {
            s = s.substring(1);
            length--;
        }

        if (length > __ParseLong.maxLengthForRadix[radix]) {
            throw new NumberFormatException("Invalide Input String :"+s);
        }

        // Validate the digits
        int maxNumericDigit = '0' + Math.min(radix, 10);
        int maxLowerCaseDigit = radix + 'a' - 10;
        int maxUpperCaseDigit = radix + 'A' - 10;
        for (int i = 0; i < length; i++) {
            char c = s.charAt(i);
            if (c >= '0' && c < maxNumericDigit) {
                continue;
            }
            if (c >= 'a' && c < maxLowerCaseDigit) {
                continue;
            }
            if (c >= 'A' && c < maxUpperCaseDigit) {
                continue;
            }
            throw new NumberFormatException("Invalide Input String :"+s);
        }

        long value = 0;
        int maxDigits = __ParseLong.maxDigitsForRadix[radix];
        long radixPower = __ParseLong.maxDigitsRadixPower[radix];
        long maxValue = __ParseLong.maxValueForRadix[radix];

        boolean firstTime = true;
        int head = length % maxDigits;
        if (head > 0) {
            value = nativeParseInt(s.substring(0, head), radix);
            s = s.substring(head);
            length -= head;
            firstTime = false;
        }

        while (length >= maxDigits) {
            head = nativeParseInt(s.substring(0, maxDigits), radix);
            s = s.substring(maxDigits);
            length -= maxDigits;
            if (!firstTime) {
                if (value > maxValue) {
                    throw new NumberFormatException(s);
                }
                value *= radixPower;
            } else {
                firstTime = false;
            }
            value += head;
        }
        if (value < 0) {
            throw new NumberFormatException("Invalide Input String :"+s);
        }

        if (negative) {
            value = -value;
        }
        return value;
    }

    private static native boolean nativeIsNaN(double x) /*-{
        return isNaN(x);
    }-*/;

    private static native double nativeParseDouble(String s) /*-{
    var rex = @java.lang.Number::floatRegex;
    if (!rex) 
        rex = @java.lang.Number::floatRegex = /^\s*[+-]?((\d+\.?\d*)|(\.\d+))([eE][+-]?\d+)?[dDfF]?\s*$/i;   
    if(rex.test(s)) 
        return parseFloat(s);
    return Number.NaN;    
    }-*/;

    private static native int nativeParseInt(String s, int radix) /*  -{
        return parseInt(s, radix);
    }-*/;

    public byte byteValue() {
        return (byte) intValue();
    }

    public abstract double doubleValue();

    public abstract float floatValue();

    public abstract int intValue();

    public abstract long longValue();

    public short shortValue() {
        return (short) intValue();
    }
}
